package com.chucang.shucang.sms.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.IdUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.chucang.shucang.common.base.constant.CommonConstant;
import com.chucang.shucang.common.base.constant.SecurityConstant;
import com.chucang.shucang.common.base.exception.FastException;
import com.chucang.shucang.common.base.redis.RedisUtil;
import com.chucang.shucang.common.base.redis.vo.PanicPurchaseProductRedisVO;
import com.chucang.shucang.common.base.service.impl.CrudServiceImpl;
import com.chucang.shucang.common.base.utils.CaffeineUtil;
import com.chucang.shucang.common.base.utils.OpsUtil;
import com.chucang.shucang.common.base.utils.R;
import com.chucang.shucang.sms.constant.SmsCacheConstant;
import com.chucang.shucang.sms.constant.SmsCommonConstant;
import com.chucang.shucang.sms.dao.PanicPurchaseDao;
import com.chucang.shucang.sms.dto.PanicPurchaseDTO;
import com.chucang.shucang.sms.entity.PanicPurchaseEntity;
import com.chucang.shucang.sms.feign.PmsService;
import com.chucang.shucang.sms.feign.UserService;
import com.chucang.shucang.sms.feign.vo.BlindBoxBaseEntity;
import com.chucang.shucang.sms.feign.vo.CollectionBaseEntity;
import com.chucang.shucang.sms.feign.vo.UserBaseEntity;
import com.chucang.shucang.sms.service.PanicPurchaseService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RSemaphore;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Service;

import java.util.*;


/**
 * 商品预售表
 *
 * @author flitsneak flitsneak@gmail.com
 * @since 1.0.0 2022-09-12
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class PanicPurchaseServiceImpl extends CrudServiceImpl<PanicPurchaseDao, PanicPurchaseEntity, PanicPurchaseDTO> implements PanicPurchaseService {

    private final PmsService pmsService;
    private final UserService userService;
    private final RedisUtil redisUtil;
    private final RedissonClient redissonClient;
    private final CaffeineUtil caffeineUtil;

    @Override
    public void onShelveProducts() {
        DateTime beginOfDay = this.getBeginOfDay();
        DateTime endOfDay = this.getEndOfDay();
        //同步库存，清理历史数据
        this.clearOldRedisInfo(beginOfDay);
        LambdaQueryWrapper<PanicPurchaseEntity> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.gt(PanicPurchaseEntity::getStartTime, beginOfDay)
                .lt(PanicPurchaseEntity::getStartTime, endOfDay);
        List<PanicPurchaseEntity> list = this.list(lambdaQueryWrapper);
        if (CollUtil.isEmpty(list)) {
            return;
        }
        list.forEach(c -> {
            //上架商品类型
            if (c.getProductType() == SmsCommonConstant.COLLECTION) {
                long begin = c.getStartTime().getTime();
                long end = c.getEndTime().getTime();
                if (!redisUtil.hasSetKey(SmsCacheConstant.PRODUCT_ON_SHELVE, String.format(SmsCacheConstant.COLLECTION_ON_SHELVE, c.getProductId(), begin, end))) {
                    R<CollectionBaseEntity> collectionBaseInfo = pmsService.getCollectionBaseInfo(c.getProductId(), SecurityConstant.FROM_IN);
                    CollectionBaseEntity collectionBaseEntity = OpsUtil
                            .of(collectionBaseInfo)
                            .assertDataNotNull(e -> new FastException("查询上架藏品信息失败！"))
                            .getData()
                            .get();
                    log.info("藏品开始定时上架，collectionId={}", collectionBaseEntity.getId());
                    //发布者信息
                    R<UserBaseEntity> infoById = userService.getUserBaseInfoById(collectionBaseEntity.getPublisherId(), SecurityConstant.FROM_IN);
                    UserBaseEntity userBaseEntity = OpsUtil
                            .of(infoById)
                            .assertDataNotNull(e -> new FastException("查询用户信息失败！"))
                            .getData()
                            .get();
                    String uuid = IdUtil.simpleUUID();
                    //基础信息存入redis
                    PanicPurchaseProductRedisVO redisVO = PanicPurchaseProductRedisVO.builder()
                            .productType(c.getProductType())
                            .detailPicture(collectionBaseEntity.getDetailPicture())
                            .coverPicture(collectionBaseEntity.getCoverPicture())
                            .productId(collectionBaseEntity.getId())
                            .totalNum(collectionBaseEntity.getTotalNum())
                            .productName(collectionBaseEntity.getDcName())
                            .productDisplay(collectionBaseEntity.getDcDisplay())
                            .productDescription(collectionBaseEntity.getDcDescription())
                            .productDisplayType(collectionBaseEntity.getDcDisplayType())
                            .productPrice(collectionBaseEntity.getDcPrice())
                            .publisherId(collectionBaseEntity.getPublisherId())
                            .username(userBaseEntity.getUsername())
                            .perLimit(c.getPerLimit())
                            .token(uuid)
                            .startTime(begin)
                            .endTime(end)
                            .build();
                    RSemaphore semaphore = redissonClient.getSemaphore(String.format(SmsCacheConstant.PRE_ON_SELL_STOCK, uuid));
                    semaphore.trySetPermits(collectionBaseEntity.getTotalNum());
                    log.info("藏品信号量设置成功！");
                    //库存编号
                    List<Integer> collList = new ArrayList<>(1600);
                    for (int i = 0; i < collectionBaseEntity.getTotalNum(); i++) {
                        collList.add(i);
                    }
                    Collections.shuffle(collList);
                    redisUtil.pushListValues(String.format(SmsCacheConstant.COLLECTION_SERIAL_NUMS, collectionBaseEntity.getId()), collList);
                    redisUtil.setHashValue(SmsCacheConstant.PRE_ON_SELL_DETAILS, String.format(SmsCacheConstant.COLLECTION_ON_SHELVE, c.getProductId(), begin, end), redisVO);
                    //场次信息加入缓存
                    redisUtil.setSetValues(SmsCacheConstant.PRODUCT_ON_SHELVE, String.format(SmsCacheConstant.COLLECTION_ON_SHELVE, c.getProductId(), begin, end));
                    log.info("藏品定时上架结束");
                }
            } else if (c.getProductType() == SmsCommonConstant.BLIND_BOX) {
                long begin = c.getStartTime().getTime();
                long end = c.getEndTime().getTime();
                if (!redisUtil.hasSetKey(SmsCacheConstant.PRODUCT_ON_SHELVE, String.format(SmsCacheConstant.BLIND_BOX_ON_SHELVE, c.getProductId(), begin, end))) {
                    R<BlindBoxBaseEntity> blindBoxBaseInfo = pmsService.getBlindBoxBaseInfo(c.getProductId(), SecurityConstant.FROM_IN);
                    BlindBoxBaseEntity blindBoxBaseEntity = OpsUtil
                            .of(blindBoxBaseInfo)
                            .assertDataNotNull(e -> new FastException("查询上架盲盒信息为空！"))
                            .getData()
                            .get();
                    log.info("盲盒开始定时上架，bbId={}", blindBoxBaseEntity.getId());
                    //发布者信息
                    R<UserBaseEntity> infoById = userService.getUserBaseInfoById(blindBoxBaseEntity.getPublisherId(), SecurityConstant.FROM_IN);
                    UserBaseEntity userBaseEntity = OpsUtil
                            .of(infoById)
                            .assertDataNotNull(e -> new FastException("查询用户信息失败！"))
                            .getData()
                            .get();
                    String uuid = IdUtil.simpleUUID();
                    //基础信息存入redis
                    PanicPurchaseProductRedisVO redisVO = PanicPurchaseProductRedisVO.builder()
                            .productType(c.getProductType())
                            .detailPicture(blindBoxBaseEntity.getDetailPicture())
                            .coverPicture(blindBoxBaseEntity.getCoverPicture())
                            .productId(blindBoxBaseEntity.getId())
                            .totalNum(blindBoxBaseEntity.getTotalNum())
                            .productName(blindBoxBaseEntity.getBbName())
                            .productDisplay(blindBoxBaseEntity.getBbDisplay())
                            .productDescription(blindBoxBaseEntity.getBbDescription())
                            .productDisplayType(blindBoxBaseEntity.getBbDisplayType())
                            .productPrice(blindBoxBaseEntity.getBbPrice())
                            .publisherId(blindBoxBaseEntity.getPublisherId())
                            .username(userBaseEntity.getUsername())
                            .perLimit(c.getPerLimit())
                            .token(uuid)
                            .startTime(begin)
                            .endTime(end)
                            .build();
                    RSemaphore semaphore = redissonClient.getSemaphore(String.format(SmsCacheConstant.PRE_ON_SELL_STOCK, uuid));
                    semaphore.trySetPermits(blindBoxBaseEntity.getTotalNum());
                    log.info("盲盒信号量设置成功！");
                    //库存编号
                    List<Integer> collList = new ArrayList<>(1600);
                    for (int i = 0; i < blindBoxBaseEntity.getTotalNum(); i++) {
                        collList.add(i);
                    }
                    Collections.shuffle(collList);
                    redisUtil.pushListValues(String.format(SmsCacheConstant.BLIND_BOX_SERIAL_NUMS, blindBoxBaseEntity.getId()), collList);
                    redisUtil.setHashValue(SmsCacheConstant.PRE_ON_SELL_DETAILS, String.format(SmsCacheConstant.BLIND_BOX_ON_SHELVE, c.getProductId(), begin, end), redisVO);
                    //场次信息加入缓存
                    redisUtil.setSetValues(SmsCacheConstant.PRODUCT_ON_SHELVE, String.format(SmsCacheConstant.BLIND_BOX_ON_SHELVE, c.getProductId(), begin, end));
                    log.info("盲盒定时上架完毕");
                }
            }
        });
    }

    private void clearOldRedisInfo(DateTime beginOfDay) {
        LambdaQueryWrapper<PanicPurchaseEntity> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.lt(PanicPurchaseEntity::getEndTime, beginOfDay);
        List<PanicPurchaseEntity> list = this.list(lambdaQueryWrapper);
        if (CollUtil.isEmpty(list)) {
            return;
        }
        //收集抢购表过期信息
        List<Long> oldList = new ArrayList<>(8);
        list.forEach(c -> {
            long begin = c.getStartTime().getTime();
            long end = c.getEndTime().getTime();
            oldList.add(c.getId());
            //清理上架set数据
            if (c.getProductType() == SmsCommonConstant.COLLECTION) {
                if (!redisUtil.hasKey(String.format(SmsCacheConstant.COLLECTION_SERIAL_NUMS, c.getProductId()))) {
                    return;
                }
                log.info("开始清理藏品遗留数据，藏品id={}", c.getProductId());
                redisUtil.removeSet(SmsCacheConstant.PRODUCT_ON_SHELVE, String.format(SmsCacheConstant.COLLECTION_ON_SHELVE, c.getProductId(), begin, end));
                //同步余量库存
                PanicPurchaseProductRedisVO redisVO = (PanicPurchaseProductRedisVO) redisUtil.getHashValue(SmsCacheConstant.PRE_ON_SELL_DETAILS, String.format(SmsCacheConstant.COLLECTION_ON_SHELVE, c.getProductId(), begin, end));
                //清除hash
                redisUtil.removeHash(SmsCacheConstant.PRE_ON_SELL_DETAILS, String.format(SmsCacheConstant.COLLECTION_ON_SHELVE, c.getProductId(), begin, end));
                //清理list
                redisUtil.deleteKey(String.format(SmsCacheConstant.COLLECTION_SERIAL_NUMS, c.getProductId()));
                Long collectionId = redisVO.getProductId();
                Object object = redisUtil.getObject(String.format(SmsCacheConstant.PRE_ON_SELL_STOCK, redisVO.getToken()));
                redisUtil.deleteKey(String.format(SmsCacheConstant.PRE_ON_SELL_STOCK, redisVO.getToken()));
                if (Objects.nonNull(object)) {
                    int surplus = (int) object;
                    pmsService.updateCollectionBase(collectionId, surplus, SecurityConstant.FROM_IN);
                }
                caffeineUtil.remove("sw" + collectionId);
            } else if (c.getProductType() == SmsCommonConstant.BLIND_BOX) {
                if (!redisUtil.hasKey(String.format(SmsCacheConstant.BLIND_BOX_SERIAL_NUMS, c.getProductId()))) {
                    return;
                }
                log.info("开始清理盲盒遗留数据，盲盒id={}", c.getProductId());
                redisUtil.removeSet(SmsCacheConstant.PRODUCT_ON_SHELVE, String.format(SmsCacheConstant.BLIND_BOX_ON_SHELVE, c.getProductId(), begin, end));
                //同步余量库存
                PanicPurchaseProductRedisVO redisVO = (PanicPurchaseProductRedisVO) redisUtil.getHashValue(SmsCacheConstant.PRE_ON_SELL_DETAILS, String.format(SmsCacheConstant.BLIND_BOX_ON_SHELVE, c.getProductId(), begin, end));
                //清除hash
                redisUtil.removeHash(SmsCacheConstant.PRE_ON_SELL_DETAILS, String.format(SmsCacheConstant.BLIND_BOX_ON_SHELVE, c.getProductId(), begin, end));
                //清理list
                redisUtil.deleteKey(String.format(SmsCacheConstant.BLIND_BOX_SERIAL_NUMS, c.getProductId()));
                Long bbId = redisVO.getProductId();
                Object object = redisUtil.getObject(String.format(SmsCacheConstant.PRE_ON_SELL_STOCK, redisVO.getToken()));
                redisUtil.deleteKey(String.format(SmsCacheConstant.PRE_ON_SELL_STOCK, redisVO.getToken()));
                if (Objects.nonNull(object)) {
                    int surplus = (int) object;
                    pmsService.updateBlindBoxBase(bbId, surplus, SecurityConstant.FROM_IN);
                }
                caffeineUtil.remove("sw" + bbId);
            }
        });
        LambdaUpdateWrapper<PanicPurchaseEntity> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper
                .set(PanicPurchaseEntity::getDelFlag, CommonConstant.DELETE)
                .in(PanicPurchaseEntity::getId, oldList);
        this.update(updateWrapper);
    }

    private DateTime getBeginOfDay() {
        return DateUtil.beginOfDay(new Date());
    }

    private DateTime getEndOfDay() {
        DateTime dateTime = DateUtil.offsetDay(new Date(), 2);
        return DateUtil.endOfDay(dateTime);
    }
}